package controllers

import java.io.File
import java.nio.file.Files
import dao._

import javax.inject.Inject
import play.api.cache.SyncCacheApi
import play.api.mvc.{AbstractController, ControllerComponents}
import tool.Pojo.Info
import tool.{FormTool, Tool}
import utils.Utils

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.language.postfixOps
import implicits.Implicits._
import org.joda.time.DateTime
import play.api.i18n.I18nSupport
import upickle.default._

/**
 * Created by yz on 3/7/2020
 */
class UserController @Inject()(cc: ControllerComponents)(
  implicit val userDao: UserDao,
  implicit val cache: SyncCacheApi,
  implicit val modedao: ModeDao,
  implicit val signInDao: SignInDao,
  implicit val personInfoDao: PersonInfoDao,

) extends AbstractController(cc) with I18nSupport {

  def logout = Action { implicit request =>
    val messages = request.messages
    Redirect(routes.AppController.loginBefore).flashing("info" -> messages("logoutSuccessfully")).
      removingFromSession("user")
  }

  def userProfileBefore = Action { implicit request =>
    val kind = request.lang.code
    kind match {
      case "en" => Ok(views.html.en.user.profile.userProfile())
      case _ => Ok(views.html.user.profile.userProfile())
    }
  }

  def getUserById = Action.async { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    userDao.selectById(data.id).map { x =>
      val json = writeJs(x)
      Ok(json)
    }
  }

  def updatePersonInfoBefore = Action { implicit request =>
    val kind = request.lang.code
    kind match {
      case "en" => Ok(views.html.en.user.profile.update())
      case _ => Ok(views.html.user.profile.update())
    }
  }

  def updatePersonInfo = Action.async { implicit request =>
    val messages = request.messages
    val data = FormTool.updateUserForm.bindFromRequest().get
    userDao.selectByEmailSome(data.email).flatMap { dbUser =>
      val row = dbUser.copy(fullName = data.fullName, institution = data.institution, province = data.province,
        city = data.city, teacher = data.teacher)
      userDao.update(row).map { x =>
        Redirect(routes.UserController.updatePersonInfoBefore).flashing("info" -> s"${messages("editProfileInformationSuccess")}!",
          "class" -> Utils.successClass).addingToSession("user" -> data.fullName,
          "id" -> dbUser.id.toString)
      }
    }
  }

  def securitySetBefore = Action { implicit request =>
    Ok(views.html.user.profile.securitySet())
  }

  def phoneChangeBefore = Action { implicit request =>
    Ok(views.html.user.profile.phoneChange())
  }

  def phoneChangeCheck = Action.async {
    implicit request =>
      val data = FormTool.newPhoneForm.bindFromRequest.get
      val userId = Tool.getUserId
      userDao.selectOpByPhone(data.newPhone).map {
        case optionUser =>
          optionUser match {
            case Some(y) =>
              val valid = if (y.id == userId) {
                true
              } else false
              Ok(ujson.Obj("valid" -> valid))
            case None =>
              val valid = true
              Ok(ujson.Obj("valid" -> valid))
          }
      }
  }

  def updatePhone = Action.async { implicit request =>
    val data = FormTool.phoneForm.bindFromRequest().get
    val userId = Tool.getUserId
    userDao.selectById(userId).flatMap { x =>
      val row = x.copy(phone = data.phone)
      userDao.update(row).map { y =>
        Ok(writeJs("success!"))
      }
    }
  }

  def guideBefore = Action { implicit request =>
    Ok(views.html.user.guide())
  }

  def emailChangeBefore = Action { implicit request =>
    Ok(views.html.user.profile.emailChange())
  }

  def emailChangeCheck = Action.async {
    implicit request =>
      val data = FormTool.emailForm.bindFromRequest.get
      val userId = Tool.getUserId
      userDao.selectOpByEmail(data.email).map {
        case optionUser =>
          optionUser match {
            case Some(y) =>
              val valid = if (y.id == userId) {
                true
              } else false
              Ok(ujson.Obj("valid" -> valid))
            case None =>
              val valid = true
              Ok(ujson.Obj("valid" -> valid))
          }
      }
  }

  def sendEmailValidCode = Action { implicit request =>
    val messages = request.messages
    val data = FormTool.emailForm.bindFromRequest().get
    Future {
      val validCode = Utils.productValidCode
      cache.set(data.email, validCode, 10 minutes)
      val content = messages("emailCodeTemplate", validCode)
      val info = Info(messages("iMapEmailVerification"), content)
      val inbox = data.email
      Utils.sendEmailBySsl(Utils.sender, info, inbox)
    }
    Ok(ujson.Obj("valid" -> true))
  }

  def validEmailCode = Action { implicit request =>
    val messages = request.messages
    val data = FormTool.validEmailCodeForm.bindFromRequest().get
    if (cache.get(data.email).isDefined && cache.get(data.email).get == data.validCode) {
      cache.remove(data.email)
      Ok(ujson.Obj("valid" -> true))
    } else {
      Ok(ujson.Obj("valid" -> false, "message" -> messages("ErrorInVerificationCode")))
    }
  }

  def updateEmail = Action.async { implicit request =>
    val data = FormTool.emailForm.bindFromRequest().get
    val userId = Tool.getUserId
    userDao.selectById(userId).flatMap { x =>
      val row = x.copy(email = data.email)
      userDao.update(row).map { y =>
        Ok(writeJs("success!"))
      }
    }
  }

  def passwordChangeBefore = Action { implicit request =>
    Ok(views.html.user.profile.passwordChange())
  }

  def updatePassword = Action.async { implicit request =>
    val data = FormTool.newPasswordForm.bindFromRequest().get
    val userId = Tool.getUserId
    userDao.selectById(userId).flatMap { x =>
      val row = x.copy(password = data.password)
      userDao.update(row).map { y =>
        Redirect(routes.UserController.securitySetBefore)
      }
    }
  }

  def updateImage = Action.async(parse.multipartFormData) { implicit request =>
    val userId = Tool.getUserId
    val tmpDir = Tool.createTempDirectory("tmpDir")
    userDao.selectById(userId).map { x =>
      val file = Tool.fileMoveDir("file", tmpDir)
      val avatarFile = Tool.getAvatarFile(userId)
      file.copyTo(avatarFile)
      Tool.deleteDirectory(tmpDir)
      Ok(ujson.Obj("valid" -> true))
    }
  }

  def getAvatarImage = Action { implicit request =>
    val userId = Tool.getUserId
    val ifModifiedSinceStr = request.headers.get(IF_MODIFIED_SINCE)
    val file = Tool.getAvatarFile(userId)
    val trueFile = if (file.exists()) {
      file
    } else {
      Tool.getDefaultAvatarFile
    }
    val lastModifiedStr = trueFile.lastModified().toString
    val MimeType = "image/png"
    val byteArray = Files.readAllBytes(trueFile.toPath)
    if (ifModifiedSinceStr.isDefined && ifModifiedSinceStr.get == lastModifiedStr) {
      NotModified
    } else {
      Ok(byteArray).as(MimeType).withHeaders(LAST_MODIFIED -> trueFile.lastModified().toString)
    }
  }

  def signInBefore = Action { implicit request =>
    Ok(views.html.user.signIn())
  }

  def signIn = Action.async { implicit request =>
    val userId = Tool.getUserId
    signInDao.select(userId).flatMap { row =>
      val info = row.info
      val now = new DateTime()
      val date = now.toString("yyyy.MM")
      val today = now.getDayOfMonth
      val daysOp = info(date).arrOpt
      val curDays = daysOp.map { days =>
        (today :: days.toList.map(_.num.toInt)).distinct
      }.getOrElse(List(today))
      val newInfo = info.toObj ++ ujson.Obj(date -> curDays)
      val newRow = row.copy(info = newInfo)
      val signInF = signInDao.update(newRow)
      val coinF = personInfoDao.select(userId).flatMap { row =>
        val newRow = row.copy(coin = row.coin + 100)
        personInfoDao.update(newRow)
      }
      signInF.zip(coinF)
    }.map { x =>
      Ok(writeJs("success"))
    }
  }

  def getHasSignInCurMonth = Action.async { implicit request =>
    val userId = Tool.getUserId
    signInDao.select(userId).map { row =>
      val info = row.info
      val now = new DateTime()
      val date = now.toString("yyyy.MM")
      val daysOp = info.obj.get(date).map(_.arr)
      val days = daysOp.map(_.toList).getOrElse(List.empty)
      Ok(writeJs(days))
    }
  }

  def getCoin = Action.async { implicit request =>
    val userId = Tool.getUserId
    personInfoDao.select(userId).map { row =>
      val coin = row.coin
      Ok(writeJs(coin))
    }
  }

  def getHasSignInInfo = Action.async { implicit request =>
    val userId = Tool.getUserId
    signInDao.select(userId).map { row =>
      val info = row.info
      Ok(info)
    }
  }

}
